package at.fhj.pse.presentation;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;

import org.primefaces.event.SelectEvent;
import org.primefaces.model.chart.Axis;
import org.primefaces.model.chart.AxisType;
import org.primefaces.model.chart.CategoryAxis;
import org.primefaces.model.chart.ChartSeries;
import org.primefaces.model.chart.LineChartModel;

import at.fhj.pse.datasource.exception.DatasourceLayerException;
import at.fhj.pse.datasource.model.Activity;
import at.fhj.pse.datasource.model.Employee;
import at.fhj.pse.datasource.model.TimeInterval;
import at.fhj.pse.domain.EmployeeBean;
import at.fhj.pse.domain.TimeIntervalBean;
import at.fhj.pse.domain.exception.DomainLayerException;
import at.fhj.pse.domain.service.DayIntervalData;
import at.fhj.pse.domain.service.IntervalService;

@ManagedBean(name = "timeIntervalView")
@ViewScoped
public class TimeIntervalView implements Serializable {

	private static final long serialVersionUID = -2310589916628130901L;

	@EJB(beanName = "TimeIntervalBeanImpl")
	@ManagedProperty("#{timeIntervalBean}")
	private TimeIntervalBean timeIntervalBean;

	@EJB(beanName = "EmployeeBeanImpl")
	@ManagedProperty("#{employeeBean}")
	private EmployeeBean employeeBean;

	private Employee employee;
	private Long employeeId;

	private Long id;

	private IntervalService intervalService;

	private Activity activity;

	private String selectedActivity;

	private String comment;

	private Date day;

	private Date fromDate;
	private Date toDate;

	private List<String> months;
	private String month;
	private List<Integer> years;
	private int year;
	private Date timeSum = new Date(0);

	private List<TimeInterval> allEmployeeIntervals = new ArrayList<TimeInterval>();
	private LineChartModel timeLineModel;

	private TimeInterval selectedTimeInterval;
	private List<TimeInterval> selectedTimeIntervals;

	private boolean dataReceived = false;

	public TimeIntervalView() {
		FacesContext context = FacesContext.getCurrentInstance();
		try {
			this.id = (long) context.getExternalContext().getSessionMap()
					.get("currentUserId");
		} catch (NullPointerException ex) {
			this.id = null;
		}
	}

	public Long getEmployeeId() {
		return this.employeeId;
	}

	public void setEmployeeId(Long id) {
		this.employeeId = id;
		retrieve();
	}

	@PostConstruct
	public void init() {

		months = new ArrayList<String>();
		months.add("January");
		months.add("February");
		months.add("March");
		months.add("April");
		months.add("May");
		months.add("June");
		months.add("July");
		months.add("August");
		months.add("September");
		months.add("October");
		months.add("November");
		months.add("December");

		Calendar cal = Calendar.getInstance();
		years = new ArrayList<Integer>();

		years.add(cal.get(Calendar.YEAR));
		years.add(cal.get(Calendar.YEAR) - 1);

		try {
			this.setEmployeeId((long) FacesContext.getCurrentInstance()
					.getExternalContext().getSessionMap().get("currentUserId"));

		} catch (NullPointerException ex) {
			this.employeeId = null;
		}
	}

	public void retrieve() {
		this.employee = employeeBean.getEmployeeById(id);
	}

	public TimeIntervalBean getTimeIntervalBean() {
		return timeIntervalBean;
	}

	public void setTimeIntervalBean(TimeIntervalBean timeIntervalBean) {
		this.timeIntervalBean = timeIntervalBean;
	}

	public EmployeeBean getEmployeeBean() {
		return employeeBean;
	}

	public void setEmployeeBean(EmployeeBean employeeBean) {
		this.employeeBean = employeeBean;
	}

	public Employee getEmployee() {
		return this.employee;
	}

	public void getIntervalsByDay() {
		try {
			this.intervalService = timeIntervalBean.getIntervalServiceByDay(
					this.day, this.employee);
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
	}

	public void displayErrMsgUncheckedException() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"Unbekannter Fehler ist aufgetreten!"));
	}

	public void getIntervalsByMonthAndYear() {
		try {
			this.intervalService = timeIntervalBean
					.getIntervalServiceByMonthAndYear(this.employee, this.month,
							this.year);
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
	}

	public void getIntervalsByActivity() {
		try {
			boolean toGreaterThanFrom = timeIntervalBean.validateToGreaterThanFrom(
					this.toDate, this.fromDate);
			if (!toGreaterThanFrom) {
				this.displayToDateEarlierThanFromDateMsg();
				return;
			}

			this.intervalService = timeIntervalBean.getIntervalServiceByActivity(
					this.employee, this.toDate, this.fromDate, this.activity);
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
	}

	public void getIntervalsByDates() {
		try {
			boolean toGreaterThanFrom = timeIntervalBean.validateToGreaterThanFrom(
					this.toDate, this.fromDate);
			if (!toGreaterThanFrom) {
				this.displayToDateEarlierThanFromDateMsg();
				return;
			}

			intervalService = timeIntervalBean.getIntervalServiceByDates(
					this.employee, this.toDate, this.fromDate);
			createLineModel();
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
	}

	public Date getDay() {
		return day;
	}

	public void setDay(Date dayOfIntervals) {
		this.day = dayOfIntervals;
	}

	public Date getTimeSum() {
		return this.timeSum;
	}

	public void setTimeSum(Date timeSum) {
		this.timeSum = timeSum;
	}

	public Date getFrom_Date() {
		return this.fromDate;
	}

	public void setFrom_Date(Date fromDate) {
		if (fromDate == null)
			displayNoDateInputMsg();
		else {
			this.fromDate = fromDate;
		}
	}

	public Date getTo_Date() {
		return this.toDate;
	}

	public void setTo_Date(Date toDate) {
		if (toDate == null)
			displayNoDateInputMsg();
		else {
			this.toDate = toDate;
		}
	}

	public String getComment() {
		return this.comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public List<String> getActivityNames() {
		try {
			return timeIntervalBean.getActivityNames();
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		return null;
	}

	public String getSelectedActivity() {
		return this.selectedActivity;
	}

	public void setSelectedActivity(String selectedActivity) {
		try {
			this.activity = timeIntervalBean.getActivityByName(selectedActivity);
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		this.selectedActivity = selectedActivity;
	}

	public Activity getActivity() {
		return activity;
	}

	public void setActivity(Activity activity) {
		this.activity = activity;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public Activity getTiActivity() {
		return this.activity;
	}

	public void setTiActivity(Activity tiActivity) {
		this.activity = tiActivity;
	}

	public String validate() {
		try {
			if (getTo_Date() == null || getFrom_Date() == null) {
				return "";
			} else if (timeIntervalBean.validateSameDay(this.toDate, this.fromDate) == false) {
				displayIncorrectDateInputMsg();
			} else if (this.activity == null) {
				displayNoActivityInputMsg();
			} else if (timeIntervalBean.intervalOverlap(this.employee, this.toDate,
					this.fromDate) == false) {
				displayIntervalOverlapInputMsg();
			} else {
				if (timeIntervalBean.validateToGreaterThanFrom(this.toDate,
						this.fromDate) == false) {
					displayToDateEarlierThanFromDateMsg();
				} else {
					timeIntervalBean.addTimeInterval(this.employee, this.toDate,
							this.fromDate, this.comment, this.activity);
					return "showIntervals.xhtml";
				}
			}
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		return "";
	}

	public String deleteTimeInterval(long interval_id) {
		try {
			for (TimeInterval interval : allEmployeeIntervals) {
				if (interval.getId() == interval_id) {
					timeIntervalBean.deleteTimeIntervalById(interval.getId());
				}
			}
			return "showIntervals?faces-redirect=true";
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		return "";
	}

	public void displayToDateEarlierThanFromDateMsg() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"To date has to be later than from date."));
	}

	public void displayNoDateInputMsg() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"Please select a valid date and time."));
	}

	public void displayIncorrectDateInputMsg() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"A timeinterval has to be at the same day."));
	}

	public void displayNoActivityInputMsg() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"You have to choose an activity."));
	}

	public void displayIntervalOverlapInputMsg() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!",
						"Timeinerval overlap with another timeinterval."));
	}

	private void createLineModel() {
		timeLineModel = initLineModel();
		timeLineModel.setTitle("Hour Chart");
		timeLineModel.setLegendPosition("e");
		timeLineModel.setShowPointLabels(false);
		timeLineModel.getAxes().put(AxisType.X, new CategoryAxis("Days"));
		Axis yAxis = timeLineModel.getAxis(AxisType.Y);
		yAxis.setLabel("Hours");
		yAxis.setMin(0.0f);
		yAxis.setMax(20.0f);
		yAxis.setTickFormat("%1$.2f");
		this.dataReceived = true;
	}

	public LineChartModel initLineModel() {
		try {
			LineChartModel model = new LineChartModel();

			ChartSeries allocatedSeries = new ChartSeries();
			allocatedSeries.setLabel("Allocated Time");

			ChartSeries actualSeries = new ChartSeries();
			actualSeries.setLabel("Actual Time");

			for (DayIntervalData di : this.intervalService.getDayIntervalDataList()) {
				Calendar cal = Calendar.getInstance();
				cal.setTime(di.getDateOfIntervals());
				String day = cal.get(Calendar.DAY_OF_MONTH) + "."
						+ (cal.get(Calendar.MONTH) + 1);

				float hoursAllocated = (float) (di.getAllocatedTimeInMinutes() / 60);
				float minutesAllocated = (float) (di.getAllocatedTimeInMinutes() - (hoursAllocated * 60));
				float timeAllocated = hoursAllocated + (minutesAllocated / 60);

				float hoursActual = (int) (di.getActualTimeInMinutes() / 60);
				float minutesActual = (int) (di.getActualTimeInMinutes() - (hoursActual * 60));
				float timeActual = hoursActual + (minutesActual / 60);

				allocatedSeries.set(day, timeAllocated);
				actualSeries.set(day, timeActual);
			}

			model.addSeries(allocatedSeries);
			model.addSeries(actualSeries);

			return model;
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		return null;
	}

	public List<TimeInterval> getAllEmployeeIntervals() {
		try {
			allEmployeeIntervals = timeIntervalBean
					.getAllEmployeeIntervals(this.employeeId);

			if (this.selectedTimeInterval == null
					&& allEmployeeIntervals.size() > 0) { // set first TimeInterval
				// as selected
				setSelectedTimeInterval(allEmployeeIntervals.get(0));
			}
			return allEmployeeIntervals;
		} catch (DatasourceLayerException | DomainLayerException ex) {
			displayErrMsgUncheckedException();
		}
		return null;
	}

	public void setAllEmployeeIntervals(List<TimeInterval> allEmployeeIntervals) {
		this.allEmployeeIntervals = allEmployeeIntervals;

	}

	public void loadAllEmployeeIntervals() {
		allEmployeeIntervals = timeIntervalBean
				.loadAllEmployeeIntervals(this.employeeId);
	}

	public void onSelect(SelectEvent event) {
		FacesContext ctx = FacesContext.getCurrentInstance();
		ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,
				"TimeInterval selected", event.getObject().toString()));
	}

	public IntervalService getIntervalService() {
		return this.intervalService;
	}

	public void setIntervalService(IntervalService intervalService) {
		this.intervalService = intervalService;
	}

	public List<String> getMonths() {
		return this.months;
	}

	public void setMonths(List<String> months) {
		this.months = months;
	}

	public String getMonth() {
		return this.month;
	}

	public void setMonth(String month) {
		this.month = month;
	}

	public List<Integer> getYears() {
		return this.years;
	}

	public void setYears(List<Integer> years) {
		this.years = years;
	}

	public int getYear() {
		return this.year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public LineChartModel getTimeLineModel() {
		return timeLineModel;
	}

	public void setTimeLineModel(LineChartModel timeLineModel) {
		this.timeLineModel = timeLineModel;
	}

	public boolean isDataReceived() {
		return dataReceived;
	}

	public void setDataReceived(boolean dataReceived) {
		this.dataReceived = dataReceived;
	}

	public TimeInterval getSelectedTimeInterval() {
		return this.selectedTimeInterval;
	}

	public void setSelectedTimeInterval(TimeInterval selectedTimeInterval) {
		this.selectedTimeInterval = selectedTimeInterval;
	}

	public List<TimeInterval> getSelectedTimeIntervals() {
		return this.selectedTimeIntervals;
	}

	public void setSelectedTimeIntervals(
			List<TimeInterval> selectedTimeIntervals) {
		this.selectedTimeIntervals = selectedTimeIntervals;
	}

	public void onRowSelect(SelectEvent event) {
		FacesMessage msg = new FacesMessage("TimeInterval selected");
		FacesContext.getCurrentInstance().addMessage(null, msg);
	}

}
